
//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
/** @author  John Miller
 *  @version 1.1
 *  @date    Fri Aug 19 17:10:23 EDT 2011
 *  @see     LICENSE (MIT style license file).
 */

package apps.event

import scalation.event.{CausalLink, Entity, Event, Model}
import scalation.model.TimeUnits._
import scalation.random.{Exponential, Variate}
import scalation.stat.Statistic

//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
/** The `CallCenter` object defines a particular scenario under which to execute
 *  the CallCenter model.
 *  @see scalation.event.ModelTest for another example of test code.
 */
object CallCenter extends App
{
    val stream     = 1                                       // random number stream to use (0 to 99)
    val lambda     = 6.0                                     // call arrival rate (per hour)
    val mu         = 7.5                                     // call service rate (per hour)
    val maxCalls   = 10                                      // stopping rule: simulate maxCalls calls
    val iArrivalRV = Exponential (HOUR/lambda, stream)       // inter-arrival time random variate
    val serviceRV  = Exponential (HOUR/mu, stream)           // service time random variate
    new CallCenterModel ("CallCenter", maxCalls, iArrivalRV, serviceRV)

} // CallCenter


//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
/** The `CallCenterModel` class defines a simple event-scheduling model of a
 *  CallCenter where service is provided by one tele-service representative and
 *  models an M/M/1/1 queue.
 *  @param name        the name of the simulation model
 *  @param nArrivals   the number of arrivals to generate (stopping condition)
 *  @param iArrivalRV  the inter-arrival time distribution (Random Variate)
 *  @param serviceRV   the service time distribution (Random Variate)
 */
class CallCenterModel (name: String, nArrivals: Int, iArrivalRV: Variate, serviceRV: Variate)
      extends Model (name, false)
{
    //:: define the state variables for the simulation

    var nArr  = 0.0    // number of calls that have arrived
    var nIn   = 0.0    // number of calls in progress
    var nOut  = 0.0    // number of calls that have finished and hung up
    var nLost = 0.0    // number of calls dropped

    val serviceTStat = new Statistic ()      // service time statistics
    val systemTStat  = new Statistic ()      // system time statistics

    //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    /** Create a subclass of `Event` for Arrival events (MakeCall).
     *  @param call  the entity that arrives, in this case a call
     */
    case class Arrival (call: Entity) extends Event (call, this)
    {
        override def occur ()
        {
            super.occur ()
            if (nArr < nArrivals-1) {
                val iArrivalT = iArrivalRV.gen
                val next2Arrive = Entity (clock + iArrivalT, serviceRV.gen)
                schedule (iArrivalT, Arrival (next2Arrive))
            } // if
            if (nIn > 0) { serviceTStat.tally (0.0); nLost += 1 }       // call dropped, no call waiting
            else           serviceTStat.tally (schedule (call.serviceT, Departure (call)))
            nArr += 1                                                   // update the current state
            nIn = 1
        } // occur

    } // Arrival class

    //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    /** Create a subclass of `Event` for Departure events (HangUp).
     *  @param call  the entity that departs, in this case a call
     */
    case class Departure (call: Entity)
         extends Event (call, this)
    {
        override def occur ()
        {
            super.occur ()
            systemTStat.tally (clock - call.arrivalT)
            nIn   = 0                                                  // update the current state
            nOut += 1
        } // occur

    } // Departure class

    //:: start the simulation, passing the first priming event and the start time

    schedule (0.0, Arrival (Entity (0.0, serviceRV.gen)))
    simulate (0.0)
    report (Array (("nArr", nArr), ("nIn", nIn), ("nLost", nLost), ("nOut", nOut)))
    reports (Array (("serviceTStat", serviceTStat), ("systemTStat", systemTStat)))

} // CallCenterModel class

